/* @flow */

import VNode, { createTextVNode } from 'core/vdom/vnode'
import { isFalse, isTrue, isDef, isUndef, isPrimitive } from 'shared/util'

// _createElement 方法传入的参数 normalizationType不同，调用不同的normalizeChildren和simpleNormalizeChildren方法

// The template compiler attempts to minimize the need for normalization by
// statically analyzing the template at compile time.
//
// For plain HTML markup, normalization can be completely skipped because the
// generated render function is guaranteed to return Array<VNode>. There are
// two cases where extra normalization is needed:

// 1. When the children contains components - because a functional component
// may return an Array instead of a single root. In this case, just a simple
// normalization is needed - if any child is an Array, we flatten the whole
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
// because functional components already normalize their own children.

// simpleNormalizeChildren调用场景是render函数是编译生成的。理论上编译生成的children都是VNode类型，但是有一个例外，
// 就是 functional components函数式组件返回的是一个数组而不是根节点，所以会通过Array.prototype.concat 方法把整个children数组打平，让它深度只有一层
// Array.prototype.concat.apply 方法的使用 https://blog.csdn.net/weixin_42752574/article/details/105745678
// concat方法的参数是一个元素，该元素会被直接插入到新数组中；如果参数是一个数组，该数组的各个元素将被插入到新数组中
// apply方法会调用一个函数，apply方法的第一个参数会作为被调用函数的this值，apply方法的第二个参数（一个数组，或类数组的对象）会作为被调用对象的arguments值
// 该数组的各个元素将会依次成为被调用函数的各个参数
// children作为apply方法的第二个参数，本身是一个数组，数组中的每一个元素（还是数组，即二维数组的第二维）会被作为参数依次传入到concat中
export function simpleNormalizeChildren (children: any) {
  for (let i = 0; i < children.length; i++) {
    if (Array.isArray(children[i])) {
      return Array.prototype.concat.apply([], children)
    }
  }
  return children
}

// 2. When the children contains constructs that always generated nested Arrays,
// e.g. <template>, <slot>, v-for, or when the children is provided by user
// with hand-written render functions / JSX. In such cases a full normalization
// is needed to cater to all possible types of children values.
// normalizeChildren 调用场景有2种，
// 1: render 函数是用户手写，当children只有一个节点的时候，Vue从接口层面允许用户把children写成基础类型用来创建单个简单的文本节点，这时会调用createTextVNode
// 2: 当编译slot、v-for的时候产生嵌套数组，会调用normalizeArrayChildren方法
export function normalizeChildren (children: any): ?Array<VNode> {
  return isPrimitive(children)
    ? [createTextVNode(children)]
    : Array.isArray(children)
      ? normalizeArrayChildren(children)
      : undefined
}

function isTextNode (node): boolean {
  return isDef(node) && isDef(node.text) && isFalse(node.isComment)
}

/**
 * @description: 遍历children，获得单个节点c，然后对c的类型判断，如果是数组，则递归调用；如果是基础类型，则通过createTextVNode方法转换成vnode类型
 * @param {*} children 要规范的子节点
 * @param {*} nestedIndex 嵌套的索引
 * @return {*}
 */
function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
  const res = []
  let i, c, lastIndex, last
  for (i = 0; i < children.length; i++) {
    c = children[i]
    if (isUndef(c) || typeof c === 'boolean') continue
    lastIndex = res.length - 1
    last = res[lastIndex]
    //  c如果是数组，则递归调用normalizeArrayChildren
    if (Array.isArray(c)) {
      if (c.length > 0) {
        c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
        // merge adjacent text nodes
        if (isTextNode(c[0]) && isTextNode(last)) {
          res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
          c.shift()
        }
        res.push.apply(res, c)
      }
    } 
    // c如果是基础类型，则通过createTextVNode方法转换成vnode类型
    else if (isPrimitive(c)) {
      if (isTextNode(last)) {
        // merge adjacent text nodes
        // this is necessary for SSR hydration because text nodes are
        // essentially merged when rendered to HTML strings
        res[lastIndex] = createTextVNode(last.text + c)
      } else if (c !== '') {
        // convert primitive to vnode
        res.push(createTextVNode(c))
      }
    } 
    // 否则就已经是、VNode类型。
    else {
      // 如果存在连个连续的text节点，会把它们合并到一个text节点
      if (isTextNode(c) && isTextNode(last)) {
        // merge adjacent text nodes
        res[lastIndex] = createTextVNode(last.text + c.text)
      } else {
        // default key for nested array children (likely generated by v-for)
        // 如果children 是一个列表且列表还存在嵌套的情况，则根据nestedIndex更新它的key
        if (isTrue(children._isVList) &&
          isDef(c.tag) &&
          isUndef(c.key) &&
          isDef(nestedIndex)) {
          c.key = `__vlist${nestedIndex}_${i}__`
        }
        res.push(c)
      }
    }
  }
  return res
}
